﻿using System.IO.Ports;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace StudySerialPort
{

    public partial class MainWindow : Window
    {
        //创建串口
        SerialPort serialPort = new SerialPort();
        public MainWindow()
        {
            InitializeComponent();

            #region SerialPort主要属性和方法讲解
            ////1、创建串口 (serialport对象)
            //SerialPort serialPort = new SerialPort();
            //serialPort.PortName = "COM1";
            //serialPort.BaudRate = 9600;//传输数据速度
            //serialPort.DataBits = 8;
            //serialPort.StopBits = StopBits.One;//停止位

            //serialPort.Parity = Parity.Odd;// 奇校验:011 校验位1
            //serialPort.ReadBufferSize = 4096;

            ////2、打开串口
            //serialPort.Open();
            ////3、发送/接收数据
            ////4、关闭串口
            #endregion

            BindPort();//绑定串口到下拉

            // 读取
            //int receiveCount = serialPort.BytesToRead;
            //byte[] bytes = new byte[receiveCount];//存放读取的数据
            //serialPort.Read(bytes, 0, receiveCount);

            //异步读取
            Task.Run(async () =>
            {
                while (true)
                {
                    _ = Dispatcher.Invoke(async () =>
                    {
                        await ReadAsync();
                    });

                    await Task.Delay(1000);
                }
            });
        }

        /// <summary>
        /// 异步读取并显示在richtextbox里
        /// </summary>
        private async Task ReadAsync()
        {
            if (serialPort.IsOpen)
            {
                // 判断接收缓冲区有数据
                if (serialPort.BytesToRead > 0)
                {
                    // 存放读的结果
                    byte[] readBytes = new byte[serialPort.BytesToRead];
                    // 接收缓冲区有数据
                    int readCount = await serialPort.BaseStream.ReadAsync(readBytes, 0, readBytes.Length);

                    if (readCount > 0)
                    {
                        // 转成字符串
                        string data = Encoding.UTF8.GetString(readBytes);

                        //ui操作
                        AppendTxt($"{DateTime.Now.ToString("yyyy-MM-dd")} 读取数据 {data}", Colors.Blue);
                    }
                }

            }
        }

        /// <summary>
        /// 绑定串口到下拉
        /// </summary>
        private void BindPort()
        {
            //绑定串口到下拉
            string[] portList = SerialPort.GetPortNames();
            cbbSpName.ItemsSource = portList;
            if (portList.Length > 0)
            {
                cbbSpName.SelectedIndex = 0;//默认选择第一个
            }
        }

        /// <summary>
        /// 打开串口
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnOpen_Click(object sender, RoutedEventArgs e)
        {
            //核心代码。输入验证
            serialPort.PortName = cbbSpName.Text;//串口名称
            serialPort.BaudRate = Convert.ToInt32(cbbBaud.Text);//波特率
            serialPort.DataBits = Convert.ToInt32(cbbDataBit.Text);//数据位 

            //校验位
            serialPort.Parity = Parity.None;
            if (cbbParity.SelectedIndex == 1)
            {
                serialPort.Parity = Parity.Odd;
            }
            if (cbbParity.SelectedIndex == 2)
            {
                serialPort.Parity = Parity.Even;
            }

            //停止位
            serialPort.StopBits = StopBits.One;
            if (cbbStopBit.SelectedIndex == 1)
            {
                serialPort.StopBits = StopBits.OnePointFive;
            }
            if (cbbStopBit.SelectedIndex == 2)
            {
                serialPort.StopBits = StopBits.Two;
            }

            //打开串口
            try
            {
                serialPort.Open();
                //隐藏打开按钮，显示关闭按钮
                BtnOpen.Visibility = Visibility.Collapsed;//隐藏打开按钮
                BtnClose.Visibility = Visibility.Visible;//显示关闭按钮

                //显示打开消息
                AppendTxt($"{DateTime.Now.ToString("yyyy-MM-dd")} 打开串口{cbbSpName.Text}成功", Colors.Green);
            }
            catch (Exception)
            {
                BtnOpen.Visibility = Visibility.Visible;
                BtnClose.Visibility = Visibility.Collapsed;

                //显示打开异常消息
                AppendTxt($"{DateTime.Now.ToString("yyyy-MM-dd")} 打开串口{cbbSpName.Text}失败", Colors.Red);
            }
        }

        /// <summary>
        /// 关闭串口
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void BtnClose_Click(object sender, RoutedEventArgs e)
        {
            if (serialPort.IsOpen)
            {
                serialPort?.Close();
                BtnOpen.Visibility = Visibility.Visible;
                BtnClose.Visibility = Visibility.Collapsed;

                //显示关闭信息
                AppendTxt($"{DateTime.Now.ToString("yyyy-MM-dd")} 关闭串口{cbbSpName.Text}", Colors.Red);
            }
        }

        FlowDocument doc = new FlowDocument();
        /// <summary>
        /// 将string追加richtextbox里
        /// </summary>
        /// <param name="txt"></param>
        /// <param name="txtColor">颜色</param>
        private void AppendTxt(string txt, Color txtColor)
        {
            var p = new Paragraph(); // Paragraph 类似于 html 的 P 标签
            var r = new Run(txt); // Run 是一个 Inline 的标签
            p.Inlines.Add(r);
            p.Foreground = new SolidColorBrush(txtColor);//设置字体颜色
            doc.Blocks.Add(p);
            txtShowContent.Document = doc;
            txtShowContent.ScrollToEnd();
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            serialPort?.Close();
        }

        private void BtnSend_Click(object sender, RoutedEventArgs e)
        {
            if (serialPort.IsOpen)//打开了才能发送数据
            {
                byte[] bytes = Encoding.UTF8.GetBytes(txtSendContent.Text);
                serialPort.Write(bytes, 0, bytes.Length);

                //显示
                AppendTxt($"{DateTime.Now.ToString("yyyy-MM-dd")} 发送消息 {txtSendContent.Text},成功", Colors.Black);
            }
            else
            {
                MessageBox.Show("请先打开串口");
            }
        }
    }
}